#include "Consulta.h"


Consulta::Consulta() {
}

Consulta::Consulta(ArchivoIndice* arch) {
    this->archIndice = arch;
}


Termino* Consulta::consultar(list<string>* query) {
    Termino* termino;
    list<string>::iterator it = query->begin();
    if (query->size() == 1)
        termino = this->buscarTermino(*it);
    else
        termino = this->buscarFrase(query);

    return termino;
}


//especie de busqueda binaria
Termino* Consulta::buscarTermino(string terminoBuscado) {
    EnteroLargo piso = 0;
    EnteroLargo techo = InfoRepositorioActual::Instance()->getCantidadBloquesFC()-1;
    EnteroLargo medio;
    Termino* termino;
    map<string, Termino*> * mapa;
    map<string, Termino*>::iterator it;
    bool encontrado = false;

    while(!encontrado && piso<=techo) {
        medio = (piso+techo)/2;
        Cabecera cabecera = archIndice->leerCabeceraBloque(medio);
        termino = cabecera.getTermino();
        if (terminoBuscado.compare(termino->getCaracteres()) == 0)
            encontrado = true;
        else
        //si el buscado es menor al medio
        if (terminoBuscado.compare(termino->getCaracteres()) < 0 )
            techo = medio-1;
        //si el buscado es mayor al medio
        else {
            mapa = archIndice->leerTerminosBloque(cabecera);
//            for (it = mapa->begin() ; it != mapa->end() ; ++it)
//                cout << it->second->getCaracteres() << endl;
            it = mapa->find(terminoBuscado);
            //si lo encontro
            if (it != mapa->end()) {
                termino = it->second;
                encontrado = true;
            }
            else
            //si no lo encontro
                piso = medio+1;
        }
    }
    //si no lo encontro devuelve puntero nulo
    if (!encontrado)
        termino = 0;

    return termino;
}



Termino* Consulta::buscarFrase(list<string>* fraseBuscada) {
    list<Termino*> terminos;
    Termino* term;
    list<string>::iterator it;
    for(it=fraseBuscada->begin(); it!=fraseBuscada->end() ; ++it) {
        string palabra = *it;
        term = this->buscarTermino(palabra);
        //si alguno de los terminos no esta, entonces no esta la frase
        if (term == 0)
            return 0;
        else
            terminos.push_back(term);
    }
    return this->intersecarTerminos(terminos);
}

//devuelve la frase buscada como si fuera un termino
Termino* Consulta::intersecarTerminos(list<Termino*>& listaTerminos) {
    string caracteres = "";
    list<Termino*>::iterator it;
    it = listaTerminos.begin();
    caracteres.append((*it)->getCaracteres());
    string salida= "";
    list<Documento*>* docs1 = (*it)->getDocumentos();
    salida = (*it)->getCaracteres();
    it++;
    caracteres.append(" ");
    caracteres.append((*it)->getCaracteres());
    list<Documento*>* docs2 = (*it)->getDocumentos();
    it++;
    list<Documento*>* intersec = this->intersecarDocumentos(docs1,docs2);
    list<Documento*>* res;
    list<Documento*>* borrar;
    while (it != listaTerminos.end() && intersec != 0) {
        caracteres.append(" ");
        caracteres.append((*it)->getCaracteres());
        docs1 = (*it)->getDocumentos();
        res = this->intersecarDocumentos(intersec,docs1);
        borrar = intersec;
        delete borrar;
        intersec = res;
        it++;
    }

    Termino* consultado = new Termino();
    consultado->setCaracteres(caracteres);
    if (intersec != 0) {
        list<Documento*>::iterator itd;
        for(itd=intersec->begin(); itd != intersec->end() ; ++itd)
            consultado->addDocumento(*itd);
    }
    else
        consultado = 0;

   return consultado;
}


//devuelve la interseccion de dos listas de documentos, si no tiene elementos en comun
//devuelve puntero nulo
list<Documento*>* Consulta::intersecarDocumentos(list<Documento*>* docs1,
                                                  list<Documento*>* docs2) {
    list<Documento*>::iterator it1 = docs1->begin();
    list<Documento*>::iterator it2 = docs2->begin();
    list<Documento*>* res = new list<Documento*>;
    list<EnteroLargo>* pos;

    while (it1 != docs1->end()  && it2 != docs2->end()) {
        if ((*it1)->getNumero() < (*it2)->getNumero())
            ++it1;
        else
        if ((*it2)->getNumero() < (*it1)->getNumero())
            ++it2;
        else
        // estan los dos terminos en el mismo doc, busco que sean pos contiguas
        {
            pos = this->sonContiguos(*it1,*it2);
            if (!pos->empty()) {
                Documento* nuevodoc = new Documento();
                nuevodoc->setNumero((*it1)->getNumero());
                list<EnteroLargo>::iterator it3;
                for (it3=pos->begin();it3!=pos->end();++it3){
                    nuevodoc->addPosicion(*it3);
                }
                res->push_back(nuevodoc);
            }
            ++it1;
            ++it2;
        }
    }

    if(res->empty())
        res = 0;

    return res;

}

//devuelve true si en los dos documentos aparecen posiciones consecutivas
list<EnteroLargo>* Consulta::sonContiguos(Documento* doc1, Documento* doc2) {
    list<EnteroLargo>* pos1 = doc1->getPosiciones();
    list<EnteroLargo>* pos2 = doc2->getPosiciones();
    list<EnteroLargo>::iterator it1 = pos1->begin();
    list<EnteroLargo>::iterator it2 = pos2->begin();
    list<EnteroLargo>* res = new list<EnteroLargo>;
    while (it1 != pos1->end()  && it2 != pos2->end()) {
        if( *it1 < *it2){
        	if(*it2 - *it1 == 1){
        	    res->push_back(*it2);
        	}
        	it1++;
        } else {
        	it2++;
		}
    }
    return res;
}


